Now, groups can retrieve a list of the contacts in that group. I probably should...
[adiumx.git] / Other / Adium Spotlight Importer / GetMetadataForFile.m
blob821c82606085d79b39d6b4c98edc846155a83f8b
1 #include <CoreFoundation/CoreFoundation.h>
2 #include <CoreServices/CoreServices.h> 
3 #import "GetMetadataForHTMLLog.h"
4 #import <AIUtilities/NSCalendarDate+ISO8601Parsing.h>
6 /*
7  Relevant keys from MDItem.h we use or may want to use:
8  
9  @constant kMDItemContentCreationDate
10  This is the date that the contents of the file were created,
11  has an application specific semantic.
12  Type is a CFDate.
14  @constant kMDItemTextContent
15  Contains the text content of the document. Type is a CFString.
17  @constant kMDItemDisplayName
18  This is the localized version of the LaunchServices call
19  LSCopyDisplayNameForURL()/LSCopyDisplayNameForRef().
21  @const  kMDItemInstantMessageAddresses
22  Instant message addresses for this item. Type is an Array of CFStrings.
23  */
25 /* -----------------------------------------------------------------------------
26 Get metadata attributes from file
28 This function's job is to extract useful information your file format supports
29 and return it as a dictionary
30 ----------------------------------------------------------------------------- */
32 Boolean GetMetadataForXMLLog(NSMutableDictionary *attributes, NSString *pathToFile);
33 NSString *GetTextContentForXMLLog(NSString *pathToFile);
35 Boolean GetMetadataForFile(void* thisInterface, 
36                                                    CFMutableDictionaryRef attributes, 
37                                                    CFStringRef contentTypeUTI,
38                                                    CFStringRef pathToFile)
40     /* Pull any available metadata from the file at the specified path */
41     /* Return the attribute keys and attribute values in the dict */
42     /* Return TRUE if successful, FALSE if there was no data provided */
43     
44         Boolean                         success = FALSE;
45         NSAutoreleasePool       *pool;
46         pool = [[NSAutoreleasePool alloc] init];
48         if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.htmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
49                 success = GetMetadataForHTMLLog((NSMutableDictionary *)attributes, (NSString *)pathToFile);
50         } else if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.xmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
51                 success = GetMetadataForXMLLog((NSMutableDictionary *)attributes, (NSString *)pathToFile);
52         } else {
53                 NSLog(@"We were passed %@, of type %@, which is an unknown type",pathToFile,contentTypeUTI);
54         }
56         [pool release];
57         
58     return success;
61 /*!
62  * @brief Copy the text content for a file
63  *
64  * This is the text which would be the kMDItemTextContent for the file in Spotlight.
65  *
66  * @param contentTypeUTI The UTI type. If NULL, the extension of pathToFile will be used
67  * @param pathToFile The full path to the file
68  *
69  * @result The kMDItemTextContent. Follows the Copy rule.
70  */
71 CFStringRef CopyTextContentForFile(CFStringRef contentTypeUTI,
72                                                                    CFStringRef pathToFile)
74         NSAutoreleasePool       *pool;
75         CFStringRef                     textContent;
76         pool = [[NSAutoreleasePool alloc] init];
77         
78         //Deteremine the UTI type if we weren't passed one
79         if (contentTypeUTI == NULL) {
80                 if (CFStringCompare((CFStringRef)[(NSString *)pathToFile pathExtension],
81                                                         CFSTR("chatLog"),
82                                                         (kCFCompareBackwards | kCFCompareCaseInsensitive)) == kCFCompareEqualTo) {
83                         contentTypeUTI = CFSTR("com.adiumx.xmllog");
84                 } else if (CFStringCompare((CFStringRef)[(NSString *)pathToFile pathExtension],
85                                                         CFSTR("AdiumXMLLog"),
86                                                         (kCFCompareBackwards | kCFCompareCaseInsensitive)) == kCFCompareEqualTo) {
87                         contentTypeUTI = CFSTR("com.adiumx.xmllog");
88                 } else {
89                         //Treat all other log extensions as HTML logs (plaintext will come out fine this way, too)
90                         contentTypeUTI = CFSTR("com.adiumx.htmllog");
91                 }
92         }
93                 
94         if (CFStringCompare(contentTypeUTI, CFSTR("com.adiumx.htmllog"), kCFCompareBackwards) == kCFCompareEqualTo) {
95                 textContent = (CFStringRef)GetTextContentForHTMLLog((NSString *)pathToFile);
96         } else if (CFStringCompare(contentTypeUTI, (CFStringRef)@"com.adiumx.xmllog", kCFCompareBackwards) == kCFCompareEqualTo) {
97                 textContent = (CFStringRef)GetTextContentForXMLLog((NSString *)pathToFile);
98                 
99         } else {
100                 textContent = nil;
101                 NSLog(@"We were passed %@, of type %@, which is an unknown type",pathToFile,contentTypeUTI);
102         }
104         if (textContent) CFRetain(textContent);
105         [pool release];
106         
107         return textContent;
111  * @brief get metadata for an XML file
113  * This function gets the metadata contained within a universal chat log format file
114  * @param attributes The dictionary in which to store the metadata
115  * @param pathToFile The path to the file to index
117  * @result true if successful, false if not
118  */
119 Boolean GetMetadataForXMLLog(NSMutableDictionary *attributes, NSString *pathToFile)
121         Boolean ret = YES;
122         NSXMLDocument *xmlDoc;
123         NSError *err=nil;
124         NSURL *furl = [NSURL fileURLWithPath:(NSString *)pathToFile];
125         xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl
126                                                                                                                                                 options:NSXMLNodePreserveCDATA
127                                                                                                                                                   error:&err];    
128         
129         if (xmlDoc)
130         {   
131                 NSArray *senderNodes = [xmlDoc nodesForXPath:@"//message/@sender"
132                                                                                                 error:&err];
133                 NSSet *duplicatesRemover = [NSSet setWithArray: senderNodes];
134                 // XPath returns an array of NSXMLNodes. Must convert them to strings containing just the attribute value.
135                 NSMutableArray *authorsArray = [NSMutableArray arrayWithCapacity:[duplicatesRemover count]];
136                 NSEnumerator *enumerator = [duplicatesRemover objectEnumerator];
137                 NSXMLNode *senderNode = nil;
138                 
139                 while( ( senderNode = [enumerator nextObject] ) ) {
140                         [authorsArray addObject:[senderNode objectValue]];
141                 }
142                 
143                 [(NSMutableDictionary *)attributes setObject:authorsArray
144                                                                                           forKey:(NSString *)kMDItemAuthors];
146                 [(NSMutableDictionary *)attributes setObject:authorsArray
147                                                                                           forKey:(NSString *)kMDItemInstantMessageAddresses];
148                 
149                 NSArray *contentArray = [xmlDoc nodesForXPath:@"//message//text()"
150                                                                                                 error:&err];
151                 NSString *contentString = [contentArray componentsJoinedByString:@" "];
152                 
153                 [attributes setObject:contentString
154                                            forKey:(NSString *)kMDItemTextContent];
156                 NSString *serviceString = [[[xmlDoc rootElement] attributeForName:@"service"] objectValue];
157                 if(serviceString != nil)
158                         [attributes setObject:serviceString
159                                                    forKey:@"com_adiumX_service"];
160                 
161                 NSArray                 *children = [[xmlDoc rootElement] children];
162                 NSCalendarDate  *startDate = nil, *endDate = nil;
164                 if ([children count]) {
165                         NSString                *dateStr;
167                         dateStr = [[(NSXMLElement *)[children objectAtIndex:0] attributeForName:@"time"] objectValue];
168                         startDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
169                         if (startDate)
170                                 [(NSMutableDictionary *)attributes setObject:startDate
171                                                                                                           forKey:(NSString *)kMDItemContentCreationDate];
173                         dateStr = [[(NSXMLElement *)[children lastObject] attributeForName:@"time"] objectValue];
174                         endDate = (dateStr ? [NSCalendarDate calendarDateWithString:dateStr] : nil);
175                         if (endDate)
176                                 [(NSMutableDictionary *)attributes setObject:[NSNumber numberWithInt:[endDate timeIntervalSinceDate:startDate]]
177                                                                                                           forKey:(NSString *)kMDItemDurationSeconds];
178                 }
180                 NSString *accountString = [[[xmlDoc rootElement] attributeForName:@"account"] objectValue];
181                 if (accountString) {
182                         [attributes setObject:accountString
183                                                    forKey:@"com_adiumX_chatSource"];
184                         NSMutableArray *otherAuthors = [authorsArray mutableCopy];
185                         [otherAuthors removeObject:accountString];
186                         [attributes setObject:otherAuthors
187                                                    forKey:@"com_adiumX_chatDestinations"];
188                         //pick the first author for this.  likely a bad idea
189                         if (startDate && [otherAuthors count]) {
190                                 NSString *toUID = [otherAuthors objectAtIndex:0];
191                                 [attributes setObject:[NSString stringWithFormat:@"%@ on %@",toUID,[startDate descriptionWithCalendarFormat:@"%Y-%m-%d"
192                                                                                                                                                                                                                                    timeZone:nil
193                                                                                                                                                                                                                                          locale:nil]]
194                                                            forKey:(NSString *)kMDItemDisplayName];
195                         }
196                         [otherAuthors release];
197                         
198                 }
199                 [attributes setObject:@"Chat log"
200                                            forKey:(NSString *)kMDItemKind];
201                 [attributes setObject:@"Adium"
202                                            forKey:(NSString *)kMDItemCreator];
203                 
204                 [xmlDoc release];
205         }
206         else
207                 ret = NO;
208         
209         return ret;
212 NSString *killXMLTags(NSString *inString)
214     NSScanner *scan = [NSScanner scannerWithString:inString];
215     NSString *tempString = nil;
216     NSMutableString *ret = [NSMutableString string];
217     NSRange rng;
218     
219     while(![scan isAtEnd]){
220         tempString = nil;
221         [scan scanUpToString:@"<" intoString:&tempString];
222         if(tempString != nil)
223             [ret appendFormat:@"%@ ", tempString];
224         [scan scanString:@"<" intoString:nil];
225         [scan scanUpToString:@">" intoString:&tempString];
226         if([tempString hasPrefix:@"br"])
227             [ret appendString:@"\n"];
228         [scan scanString:@">" intoString:nil];
229     }
230     rng.location = -1;
231     do {
232         NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
233         rng = [ret rangeOfString:@"&lt;" options:0 range:searchRange];
234         if (rng.length > 0)
235             [ret replaceCharactersInRange: rng withString: @"<"];
236     } while (rng.length > 0);
237     rng.location = -1;
238     do {
239         NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
240         rng = [ret rangeOfString:@"&gt;" options:0 range:searchRange];
241         if (rng.length > 0)
242             [ret replaceCharactersInRange: rng withString: @">"];
243     } while (rng.length > 0);
244     rng.location = -1;
245     do {
246         NSRange searchRange = NSMakeRange(rng.location+1, [ret length]-rng.location-1);
247         rng = [ret rangeOfString:@"&amp;" options:0 range:searchRange];
248         if (rng.length > 0)
249             [ret replaceCharactersInRange: rng withString: @"&"];
250     } while (rng.length > 0);
251     return ret;
254 NSString *GetTextContentForXMLLog(NSString *pathToFile)
256         NSError *err=nil;
257         NSURL *furl = [NSURL fileURLWithPath:(NSString *)pathToFile];
258         NSString *contentString;
259         NSXMLDocument *xmlDoc;
260         xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:furl
261                                                                                                   options:NSXMLNodePreserveCDATA
262                                                                                                         error:&err];    
263         NSArray *contentArray = [xmlDoc nodesForXPath:@"//message//text()"
264                                                                                         error:&err];
265         contentString = [contentArray componentsJoinedByString:@" "];
266         [xmlDoc release];
267         return contentString;